package com.xiaoshuidi.cloud.module.bpm.service.task;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.xiaoshuidi.cloud.framework.common.pojo.PageResult;
import com.xiaoshuidi.cloud.framework.common.util.date.DateUtils;
import com.xiaoshuidi.cloud.framework.common.util.number.NumberUtils;
import com.xiaoshuidi.cloud.framework.common.util.object.PageUtils;
import com.xiaoshuidi.cloud.framework.mybatis.core.query.QueryWrapperX;
import com.xiaoshuidi.cloud.framework.tenant.core.util.TenantUtils;
import com.xiaoshuidi.cloud.module.bpm.api.task.dto.BpmTaskRespDTO;
import com.xiaoshuidi.cloud.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceMyPageRespVO;
import com.xiaoshuidi.cloud.module.bpm.controller.admin.task.vo.task.*;
import com.xiaoshuidi.cloud.module.bpm.convert.task.BpmTaskConvert;
import com.xiaoshuidi.cloud.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO;
import com.xiaoshuidi.cloud.module.bpm.dal.dataobject.task.BpmTaskExtDO;
import com.xiaoshuidi.cloud.module.bpm.dal.mysql.task.BpmTaskExtMapper;
import com.xiaoshuidi.cloud.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum;
import com.xiaoshuidi.cloud.module.bpm.enums.task.BpmProcessInstanceResultEnum;
import com.xiaoshuidi.cloud.module.bpm.service.definition.BpmTaskAssignRuleService;
import com.xiaoshuidi.cloud.module.bpm.service.message.BpmMessageService;
import com.xiaoshuidi.cloud.module.system.api.dept.DeptApi;
import com.xiaoshuidi.cloud.module.system.api.dept.dto.DeptRespDTO;
import com.xiaoshuidi.cloud.module.system.api.user.AdminUserApi;
import com.xiaoshuidi.cloud.module.system.api.user.dto.AdminUserRespDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.SequenceFlow;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.HistoryService;
import org.flowable.engine.ManagementService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.NativeTaskQuery;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskInfo;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.xiaoshuidi.cloud.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.xiaoshuidi.cloud.framework.common.util.collection.CollectionUtils.convertMap;
import static com.xiaoshuidi.cloud.framework.common.util.collection.CollectionUtils.convertSet;
import static com.xiaoshuidi.cloud.module.bpm.enums.ErrorCodeConstants.*;

/**
 * 流程任务实例 Service 实现类
 *
 * @author
 * @author jason
 */
@Slf4j
@Service
public class BpmTaskServiceImpl implements BpmTaskService {

    @Resource
    private TaskService taskService;
    @Resource
    private HistoryService historyService;
    @Resource
    private RepositoryService repositoryService;
    @Resource
    private BpmProcessInstanceService processInstanceService;
    @Resource
    private AdminUserApi adminUserApi;
    @Resource
    private DeptApi deptApi;
    @Resource
    private BpmTaskExtMapper taskExtMapper;
    @Resource
    private BpmMessageService messageService;
    @Resource
    private BpmTaskAssignRuleService bpmTaskAssignRuleService;
    @Resource
    private ManagementService managementService;

    private Cache<String, List<BpmTaskRespDTO>> bpmTaskRespCache = Caffeine.newBuilder()
            .expireAfterAccess(Duration.ofMinutes(1))
            .build();

    @Override
    public long getTodoTaskCount(Long userId) {
        return taskService.createTaskQuery().taskAssignee(String.valueOf(userId)).count();
    }

    @Override
    public PageResult<BpmTaskTodoPageItemRespVO> getTodoTaskPage(Long userId, BpmTaskTodoPageReqVO pageVO) {
        // 查询待办任务
        TaskQuery taskQuery = taskService.createTaskQuery().taskAssignee(String.valueOf(userId))
                // 分配给自己
            .orderByTaskCreateTime().desc(); // 创建时间倒序
        if (StringUtils.isNotBlank(pageVO.getCategory()) ) {
//             taskQuery.taskCategory(pageVO.getCategory());
            taskQuery.processVariableValueEquals("processDefinitionCategory", pageVO.getCategory());
        }
        if (StringUtils.isNotBlank(pageVO.getKeyword())) {
//             taskQuery.taskCategory(pageVO.getCategory());
            taskQuery.or().processVariableValueLike("createName", "%" + pageVO.getKeyword() + "%")
                    .processVariableValueLike("query-customerName", "%" + pageVO.getKeyword() + "%")
                    .processVariableValueLike("query-customerPhone", "%" + pageVO.getKeyword() + "%")
                    .processVariableValueLike("query-roomName", "%" + pageVO.getKeyword() + "%");
        }
        if (StrUtil.isNotBlank(pageVO.getName())) {
            taskQuery.taskNameLike("%" + pageVO.getName() + "%");
        }
        if (ArrayUtil.get(pageVO.getCreateTime(), 0) != null) {
            taskQuery.taskCreatedAfter(DateUtils.of(pageVO.getCreateTime()[0]));
        }
        if (ArrayUtil.get(pageVO.getCreateTime(), 1) != null) {
            taskQuery.taskCreatedBefore(DateUtils.of(pageVO.getCreateTime()[1]));
        }

        taskQuery.orderByTaskCreateTime().desc(); // 创建时间倒序
        // 执行查询
        List<Task> tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize());
        log.info("审批工单查询结果:{}", JSONUtil.toJsonStr(tasks));
        if (CollUtil.isEmpty(tasks)) {
            return PageResult.empty(0L);
        }

        // 获得 ProcessInstance Map
        Map<String, ProcessInstance> processInstanceMap =
                processInstanceService.getProcessInstanceMap(convertSet(tasks, Task::getProcessInstanceId));
        // 获得 User Map
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
                convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId())));
        // 拼接结果
        List<BpmTaskTodoPageItemRespVO> bpmTaskTodoPageItemRespVOS =
                BpmTaskConvert.INSTANCE.convertList1(tasks, processInstanceMap, userMap);
        return new PageResult<>(bpmTaskTodoPageItemRespVOS, taskQuery.count());
    }

    @Override
    public PageResult<BpmTaskDonePageItemRespVO> getDoneTaskPage(Long userId, BpmTaskDonePageReqVO pageVO) {
        // 查询已办任务
        HistoricTaskInstanceQuery taskQuery;
        if (pageVO.isMyCreate()) {
            log.info("查询自己发起的 -------------");
            taskQuery = historyService.createHistoricTaskInstanceQuery().finished() // 已完成
                    .processVariableValueEquals("creator", userId);
        } else {
            log.info("查询自己审批的 -------------");
            taskQuery = historyService.createHistoricTaskInstanceQuery().finished() // 已完成
                    /* .or() // 是自己发起的或者是分配给自己的
                     .processVariableValueEquals("creator", userId)*/
                    .taskAssignee(String.valueOf(userId));
        }

        if (StrUtil.isNotBlank(pageVO.getName())) {
            taskQuery.taskNameLike("%" + pageVO.getName() + "%");
        }
        if (pageVO.getBeginCreateTime() != null) {
            taskQuery.taskCreatedAfter(DateUtils.of(pageVO.getBeginCreateTime()));
        }
        if (pageVO.getEndCreateTime() != null) {
            taskQuery.taskCreatedBefore(DateUtils.of(pageVO.getEndCreateTime()));
        }
        if (StringUtils.isNotBlank(pageVO.getCategory())) {
//             taskQuery.taskCategory(pageVO.getCategory());
            taskQuery.processVariableValueEquals("processDefinitionCategory", pageVO.getCategory());
        }
        if (StringUtils.isNotBlank(pageVO.getKeyword())) {
//             taskQuery.taskCategory(pageVO.getCategory());
            taskQuery.or().processVariableValueLike("createName", "%" + pageVO.getKeyword() + "%")
                    .processVariableValueLike("query-customerName", "%" + pageVO.getKeyword() + "%")
                    .processVariableValueLike("query-customerPhone", "%" + pageVO.getKeyword() + "%")
                    .processVariableValueLike("query-roomName", "%" + pageVO.getKeyword() + "%");
        }

        taskQuery.orderByHistoricTaskInstanceEndTime().desc(); // 审批时间倒序
        // 执行查询
        List<HistoricTaskInstance> tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize());
        if (CollUtil.isEmpty(tasks)) {
            return PageResult.empty(0L);
        }

        // 获得 TaskExtDO Map
        List<BpmTaskExtDO> bpmTaskExtDOs =
                taskExtMapper.selectListByTaskIds(convertSet(tasks, HistoricTaskInstance::getId));
        Map<String, BpmTaskExtDO> bpmTaskExtDOMap = convertMap(bpmTaskExtDOs, BpmTaskExtDO::getTaskId);
        // 获得 ProcessInstance Map
        Map<String, HistoricProcessInstance> historicProcessInstanceMap =
                processInstanceService.getHistoricProcessInstanceMap(
                        convertSet(tasks, HistoricTaskInstance::getProcessInstanceId));
        // 获得 User Map
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
                convertSet(historicProcessInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId())));

        // 拼接结果
        return new PageResult<>(BpmTaskConvert.INSTANCE.convertList2(tasks, bpmTaskExtDOMap, historicProcessInstanceMap, userMap),
                taskQuery.count());
    }


    @Override
    public BpmProcessInstanceMyPageRespVO getMyDoneTaskPage(Long userId, BpmTaskDonePageReqVO pageVO) {
        // 查询已办任务
        log.info("查询自己发起的 -------------");
        HistoricProcessInstanceQuery taskQuery = historyService.createHistoricProcessInstanceQuery().finished() // 已完成
                .variableValueEquals("creator", userId);

//        if (StrUtil.isNotBlank(pageVO.getName())) {
//            taskQuery.taskNameLike("%" + pageVO.getName() + "%");
//        }
        if (pageVO.getBeginCreateTime() != null) {
            taskQuery.finishedAfter(DateUtils.of(pageVO.getBeginCreateTime()));
        }
        if (pageVO.getEndCreateTime() != null) {
            taskQuery.finishedBefore(DateUtils.of(pageVO.getEndCreateTime()));
        }
        if (StringUtils.isNotBlank(pageVO.getCategory())) {
//             taskQuery.taskCategory(pageVO.getCategory());
            taskQuery.variableValueEquals("processDefinitionCategory", pageVO.getCategory());
        }
        if (StringUtils.isNotBlank(pageVO.getKeyword())) {
//             taskQuery.taskCategory(pageVO.getCategory());
            taskQuery.or().variableValueLike("createName", "%" + pageVO.getKeyword() + "%")
                    .variableValueLike("query-customerName", "%" + pageVO.getKeyword() + "%")
                    .variableValueLike("query-customerPhone", "%" + pageVO.getKeyword() + "%")
                    .variableValueLike("query-roomName", "%" + pageVO.getKeyword() + "%");
        }

        taskQuery.orderByProcessInstanceEndTime().desc(); // 审批时间倒序
        // 执行查询
        List<HistoricProcessInstance> tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize());
        log.info("tasks {}", JSON.toJSONString(tasks));
        PageResult pageResult = PageResult.empty(0L);
        Map<Integer, Integer> countMap = new HashMap<>();
        if (CollUtil.isNotEmpty(tasks)) {

            // 获得 TaskExtDO Map
            List<BpmTaskExtDO> bpmTaskExtDOs =
                    taskExtMapper.selectListByProcessInstanceId(convertSet(tasks, HistoricProcessInstance::getId));
            Map<String, BpmTaskExtDO> bpmTaskExtDOMap = bpmTaskExtDOs.stream().collect(Collectors.toMap(BpmTaskExtDO::getProcessInstanceId, task1 -> task1, (s1, s2) -> {
                if (s1.getId() > s2.getId()) {
                    return s1;
                }
                return s2;
            }));
            countMap = bpmTaskExtDOMap.values().stream().collect(Collectors.toMap(BpmTaskExtDO::getResult, e -> 1, Integer::sum));
            // 获得 ProcessInstance Map
            Map<String, HistoricProcessInstance> historicProcessInstanceMap =
                    processInstanceService.getHistoricProcessInstanceMap(
                            convertSet(tasks, HistoricProcessInstance::getId));
            // 获得 User Map
            Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
                    convertSet(tasks, instance -> Long.valueOf(instance.getStartUserId())));

            // 拼接结果
            pageResult = new PageResult<>(BpmTaskConvert.INSTANCE.convertList4(tasks, historicProcessInstanceMap, bpmTaskExtDOMap, userMap),
                    taskQuery.count());
        }
        BpmProcessInstanceMyPageRespVO bpmPageResult = new BpmProcessInstanceMyPageRespVO();
        bpmPageResult.setList(pageResult.getList());
        bpmPageResult.setTotal(pageResult.getTotal());
        bpmPageResult.setCount(countMap);
        return bpmPageResult;

    }

    @Override
    public List<Task> getTasksByProcessInstanceIds(List<String> processInstanceIds) {
        if (CollUtil.isEmpty(processInstanceIds)) {
            return Collections.emptyList();
        }
        return taskService.createTaskQuery().processInstanceIdIn(processInstanceIds).list();
    }

    @Override
    public List<BpmTaskRespVO> getTaskListByProcessInstanceId(String processInstanceId) {
        // 获得任务列表
        List<HistoricTaskInstance> tasks = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                .orderByHistoricTaskInstanceStartTime().desc() // 创建时间倒序
                .list();
        if (CollUtil.isEmpty(tasks)) {
            return Collections.emptyList();
        }
        HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(processInstanceId);
        return getBpmTaskRespVOList(tasks, processInstance);
    }

    private List<BpmTaskRespVO> getBpmTaskRespVOList(List<HistoricTaskInstance> tasks, HistoricProcessInstance processInstance) {
        // 获得 TaskExtDO Map
        List<BpmTaskExtDO> bpmTaskExtDOs = taskExtMapper.selectListByTaskIds(convertSet(tasks, HistoricTaskInstance::getId));
        Map<String, BpmTaskExtDO> bpmTaskExtDOMap = convertMap(bpmTaskExtDOs, BpmTaskExtDO::getTaskId);
        // 获得 ProcessInstance Map
        // 获得 User Map
        Set<Long> userIds = convertSet(tasks, task -> NumberUtils.parseLong(task.getAssignee()));
        userIds.add(NumberUtils.parseLong(processInstance.getStartUserId()));
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
        // 获得 Dept Map
        Set<Long> deptIds = convertSet(userMap.values(), AdminUserRespDTO::getDeptId);
        Map<Long, DeptRespDTO> deptMap = CollectionUtils.isEmpty(deptIds) ? new HashMap<>(0) : deptApi.getDeptMap(deptIds);
        // 拼接数据
        return BpmTaskConvert.INSTANCE.convertList3(tasks, bpmTaskExtDOMap, processInstance, userMap, deptMap);
    }

    @Override
    @Deprecated
    public List<BpmTaskRespDTO> getAllTaskListByProcessInstanceId(String processInstanceId) {
        List<BpmTaskRespDTO> cache = bpmTaskRespCache.getIfPresent(processInstanceId);
        if (cache != null) {
            return cache;
        }

        // 获得任务列表
        List<HistoricTaskInstance> tasks = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                .orderByHistoricTaskInstanceStartTime().desc() // 创建时间倒序
                .list();
        if (CollUtil.isEmpty(tasks)) {
            return Collections.emptyList();
        }
        HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(processInstanceId);
        List<BpmTaskRespVO> result = getBpmTaskRespVOList(tasks, processInstance);

        BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
        Process process = bpmnModel.getProcesses().get(0);
        List<UserTask> userTasks = process.findFlowElementsOfType(UserTask.class);
        Map<String, List<BpmTaskRespVO>> taskRespVOMap = result.stream()
                .filter(r -> !r.getResult().equals(BpmProcessInstanceResultEnum.CANCEL.getResult()))
                .collect(Collectors.groupingBy(BpmTaskRespVO::getDefinitionKey));
        List<BpmTaskAssignRuleDO> rule = bpmTaskAssignRuleService.getTaskAssignRuleListByProcessDefinitionId(processInstance.getProcessDefinitionId(), null);
        Map<String, BpmTaskAssignRuleDO> taskAssignRuleDOMap = rule.stream().collect(Collectors.toMap(BpmTaskAssignRuleDO::getTaskDefinitionKey, b -> b));

        Map<Long, BpmTaskRespDTO.User> userCacheMap = new HashMap<>(1);
        cache = userTasks.stream()
                .map(ut -> {
                    List<BpmTaskRespVO> vos = taskRespVOMap.get(ut.getId());
                    if (vos != null) {
                        BpmTaskRespVO vo = vos.get(vos.size() - 1);
                        BpmTaskRespDTO dto = BpmTaskConvert.INSTANCE.convert(vo);
                        BpmTaskRespDTO.User u = BpmTaskConvert.INSTANCE.convert(vo.getAssigneeUser());
                        dto.setAssigneeUsers(Collections.singletonList(BpmTaskConvert.INSTANCE.convert(vo.getAssigneeUser())));
                        userCacheMap.put(u.getId(), u);
                        return dto;
                    }
                    BpmTaskRespDTO vo1 = new BpmTaskRespDTO();
                    vo1.setName(ut.getName());
                    BpmTaskAssignRuleDO assignRuleDO = taskAssignRuleDOMap.get(ut.getId());
                    ExecutionEntity execution = ExecutionEntityImpl.createWithEmptyRelationshipCollections();
                    execution.setProcessInstanceId(processInstanceId);
                    Set<Long> userIds = bpmTaskAssignRuleService.calculateTaskCandidateUsers(execution, assignRuleDO);

                    List<BpmTaskRespDTO.User> l = new ArrayList<>(userIds.size());
                    Set<Long> c = userIds.stream().filter(userCacheMap::containsKey).peek(i -> l.add(userCacheMap.get(i))).collect(Collectors.toSet());
                    userIds.removeAll(c);
                    if (!userIds.isEmpty()) {
                        l.addAll(getTaskAssignees(userIds));
                    }
                    vo1.setAssigneeUsers(l);
                    return vo1;
                }).collect(Collectors.toList());

        bpmTaskRespCache.put(processInstanceId, cache);
        return cache;
    }


    @Override
    public List<BpmTaskRespDTO> getAllTaskListByProcessInstanceId2(String processInstanceId) {
        // 获得任务列表
        List<HistoricTaskInstance> tasks = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                .orderByHistoricTaskInstanceStartTime().desc() // 创建时间倒序
                .list();
        if (CollUtil.isEmpty(tasks)) {
            return Collections.emptyList();
        }
        HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(processInstanceId);
        List<BpmTaskRespVO> bpmTask = getBpmTaskRespVOList(tasks, processInstance);
        Map<String, List<BpmTaskRespVO>> taskRespVOMap = bpmTask.stream()
                .filter(r -> !BpmProcessInstanceResultEnum.CANCEL.getResult().equals(r.getResult()))
                .collect(Collectors.groupingBy(BpmTaskRespVO::getDefinitionKey));
        //过滤后为空 异常数据
        if (MapUtil.isEmpty(taskRespVOMap)) {
            return Collections.emptyList();
        }
        List<BpmTaskAssignRuleDO> rule =
                bpmTaskAssignRuleService.getTaskAssignRuleListByProcessDefinitionId(processInstance.getProcessDefinitionId(), null);
        Map<String, BpmTaskAssignRuleDO> taskAssignRuleDOMap =
                rule.stream().collect(Collectors.toMap(BpmTaskAssignRuleDO::getTaskDefinitionKey, b -> b));


        List<HistoricActivityInstance> activityList = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId).list();
        // 流程已完成路径
        Map<String, List<HistoricActivityInstance>> userTaskMap =
                activityList.stream().filter(a -> "userTask".equals(a.getActivityType()))
                        .collect(Collectors.groupingBy(HistoricActivityInstance::getActivityId));
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
        Process process = bpmnModel.getProcesses().get(0);
        List<UserTask> userTasks = process.findFlowElementsOfType(UserTask.class);

        Map<Long, BpmTaskRespDTO.User> userCacheMap = new HashMap<>(1);
        UserTask t = userTasks.get(userTasks.size() - 1);
        List<UserTask> ul = t.getOutgoingFlows().stream().map(SequenceFlow::getTargetFlowElement)
                .filter(UserTask.class::isInstance)
                .map(ut2 -> ((UserTask) ut2))
                .collect(Collectors.toList());
        if (ul.size() == 1) {
            userTasks.addAll(ul);
        }
        return userTasks.stream()
                .filter(ut -> userTaskMap.containsKey(ut.getId()))
                .map(ut -> {
                    List<BpmTaskRespVO> vos = taskRespVOMap.get(ut.getId());
                    if (vos != null) {
                        BpmTaskRespVO vo = vos.get(vos.size() - 1);
                        BpmTaskRespDTO dto = BpmTaskConvert.INSTANCE.convert(vo);
                        BpmTaskRespDTO.User u = BpmTaskConvert.INSTANCE.convert(vo.getAssigneeUser());
                        dto.setAssigneeUsers(Collections.singletonList(BpmTaskConvert.INSTANCE.convert(vo.getAssigneeUser())));
                        userCacheMap.put(u.getId(), u);
                        return dto;
                    }
                    BpmTaskRespDTO vo1 = new BpmTaskRespDTO();
                    vo1.setName(ut.getName());
                    BpmTaskAssignRuleDO assignRuleDO = taskAssignRuleDOMap.get(ut.getId());
                    ExecutionEntity execution = ExecutionEntityImpl.createWithEmptyRelationshipCollections();
                    execution.setProcessInstanceId(processInstanceId);
                    Set<Long> userIds = bpmTaskAssignRuleService.calculateTaskCandidateUsers(execution, assignRuleDO);

                    List<BpmTaskRespDTO.User> l = new ArrayList<>(userIds.size());
                    Set<Long> c = userIds.stream().filter(userCacheMap::containsKey).peek(i -> l.add(userCacheMap.get(i))).collect(Collectors.toSet());
                    userIds.removeAll(c);
                    if (!userIds.isEmpty()) {
                        l.addAll(getTaskAssignees(userIds));
                    }
                    vo1.setAssigneeUsers(l);
                    return vo1;
                })
                .collect(Collectors.toList());
    }

    private List<BpmTaskRespDTO.User> getTaskAssignees(Set<Long> userIds) {
        if (CollectionUtils.isEmpty(userIds)) {
            return Collections.emptyList();
        }
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
        // 获得 Dept Map
        Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));

        return userMap.values().stream().map(u -> {
            BpmTaskRespDTO.User ur = BpmTaskConvert.INSTANCE.convert4(u);
            DeptRespDTO dept = deptMap.get(ur.getDeptId());
            if (dept != null) {
                ur.setDeptName(dept.getName());
            }
            return ur;
        }).collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void approveTask(Long userId, @Valid BpmTaskApproveReqVO reqVO) {
        // 校验任务存在
        Task task = checkTask(userId, reqVO.getId());
        // 校验流程实例存在
        ProcessInstance instance = processInstanceService.getProcessInstance(task.getProcessInstanceId());
        if (instance == null) {
            throw exception(PROCESS_INSTANCE_NOT_EXISTS);
        }

        // 完成任务，审批通过
        taskService.complete(task.getId(), instance.getProcessVariables());

        // 更新任务拓展表为通过
        taskExtMapper.updateByTaskId(
                new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.APPROVE.getResult())
                        .setReason(reqVO.getReason()));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void rejectTask(Long userId, @Valid BpmTaskRejectReqVO reqVO) {
        Task task = checkTask(userId, reqVO.getId());
        // 校验流程实例存在
        ProcessInstance instance = processInstanceService.getProcessInstance(task.getProcessInstanceId());
        if (instance == null) {
            throw exception(PROCESS_INSTANCE_NOT_EXISTS);
        }

        // 更新流程实例为不通过
        processInstanceService.updateProcessInstanceExtReject(instance.getProcessInstanceId(), reqVO.getReason());

        // 更新任务拓展表为不通过
        taskExtMapper.updateByTaskId(
                new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.REJECT.getResult())
                        .setEndTime(LocalDateTime.now()).setReason(reqVO.getReason()));
    }

    @Override
    public void updateTaskAssignee(Long userId, BpmTaskUpdateAssigneeReqVO reqVO) {
        // 校验任务存在
        Task task = checkTask(userId, reqVO.getId());
        // 更新负责人
        updateTaskAssignee(task.getId(), reqVO.getAssigneeUserId());
    }

    @Override
    public void updateTaskAssignee(String id, Long userId) {
        taskService.setAssignee(id, String.valueOf(userId));
    }

    /**
     * 校验任务是否存在， 并且是否是分配给自己的任务
     *
     * @param userId 用户 id
     * @param taskId task id
     */
    private Task checkTask(Long userId, String taskId) {
        Task task = getTask(taskId);
        if (task == null) {
            throw exception(TASK_COMPLETE_FAIL_NOT_EXISTS);
        }
        if (!Objects.equals(userId, NumberUtils.parseLong(task.getAssignee()))) {
            throw exception(TASK_COMPLETE_FAIL_ASSIGN_NOT_SELF);
        }
        return task;
    }

    @Override
    public void createTaskExt(Task task) {
        BpmTaskExtDO taskExtDO =
                BpmTaskConvert.INSTANCE.convert2TaskExt(task).setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
        taskExtMapper.insert(taskExtDO);
    }

    @Override
    public void updateTaskExtComplete(Task task) {
        BpmTaskExtDO taskExtDO = BpmTaskConvert.INSTANCE.convert2TaskExt(task)
                .setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()) // 不设置也问题不大，因为 Complete 一般是审核通过，已经设置
                .setEndTime(LocalDateTime.now());
        taskExtMapper.updateByTaskId(taskExtDO);
    }

    @Override
    public void updateTaskExtCancel(String taskId) {
        // 需要在事务提交后，才进行查询。不然查询不到历史的原因
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {

            @Override
            public void afterCommit() {
                // 可能只是活动，不是任务，所以查询不到
                HistoricTaskInstance task = getHistoricTask(taskId);
                if (task == null) {
                    return;
                }

                // 如果任务拓展表已经是完成的状态，则跳过
                BpmTaskExtDO taskExt = taskExtMapper.selectByTaskId(taskId);
                if (taskExt == null) {
                    log.error("[updateTaskExtCancel][taskId({}) 查找不到对应的记录，可能存在问题]", taskId);
                    return;
                }
                // 如果已经是最终的结果，则跳过
                if (BpmProcessInstanceResultEnum.isEndResult(taskExt.getResult())) {
                    log.error("[updateTaskExtCancel][taskId({}) 处于结果({})，无需进行更新]", taskId, taskExt.getResult());
                    return;
                }

                // 更新任务
                taskExtMapper.updateById(new BpmTaskExtDO().setId(taskExt.getId()).setResult(BpmProcessInstanceResultEnum.CANCEL.getResult())
                        .setEndTime(LocalDateTime.now()).setReason(BpmProcessInstanceDeleteReasonEnum.translateReason(task.getDeleteReason())));
            }

        });
    }

    @Override
    public void updateTaskExtAssign(Task task) {
        BpmTaskExtDO taskExtDO =
                new BpmTaskExtDO().setAssigneeUserId(NumberUtils.parseLong(task.getAssignee())).setTaskId(task.getId());
        taskExtMapper.updateByTaskId(taskExtDO);
        // 发送通知。在事务提交时，批量执行操作，所以直接查询会无法查询到 ProcessInstance，所以这里是通过监听事务的提交来实现。
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            @Override
            public void afterCommit() {
                ProcessInstance processInstance =
                        processInstanceService.getProcessInstance(task.getProcessInstanceId());
                AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId())).getCheckedData();
                if (ObjectUtil.isNotEmpty(startUser)) {
                    messageService.sendMessageWhenTaskAssigned(
                            BpmTaskConvert.INSTANCE.convert(processInstance, startUser, task));
                }
            }
        });
    }

    @Override
    public Map<Integer, Integer> getMyTaskGroupCount(Long loginUserId, BpmTaskDonePageReqVO pageReqVO) {
        List<Map<String, Integer>> l = TenantUtils.executeIgnoreAndReturn(() -> taskExtMapper.selectByTaskGroupCount(loginUserId, pageReqVO));
        return l.stream().collect(Collectors.toMap(m -> m.get("result"), b -> b.get("count")));
    }

    @Override
    public void deleteAll(Long loginUserId) {
        Set<String> taskId = taskService.createTaskQuery().taskAssignee(String.valueOf(loginUserId)).list()
                .stream().map(TaskInfo::getId).collect(Collectors.toSet());
        if (!CollectionUtils.isEmpty(taskId)) {
            taskService.deleteTasks(taskId, "手动删除");
        }
        historyService.createHistoricTaskInstanceQuery().taskAssignee(String.valueOf(loginUserId)).finished().list()
                .forEach(t -> historyService.deleteHistoricTaskInstance(t.getId()));
        taskExtMapper.delete(new QueryWrapperX<BpmTaskExtDO>().eq("assignee_user_id", loginUserId));
    }

    private Task getTask(String id) {
        return taskService.createTaskQuery().taskId(id).singleResult();
    }

    private HistoricTaskInstance getHistoricTask(String id) {
        return historyService.createHistoricTaskInstanceQuery().taskId(id).singleResult();
    }

}
